package com.xiaoshuidi.cloud.module.finance.controller.admin.finance;

import com.alibaba.excel.util.MapUtils;
import com.xiaoshuidi.cloud.framework.common.pojo.CommonResult;
import com.xiaoshuidi.cloud.framework.common.pojo.PageResult;
import com.xiaoshuidi.cloud.framework.excel.core.util.ExcelUtils;
import com.xiaoshuidi.cloud.module.contract.api.ContractRentOrderApi;
import com.xiaoshuidi.cloud.module.contract.vo.OrderFeeDetailVO;
import com.xiaoshuidi.cloud.module.finance.controller.admin.finance.vo.flow.FinanceMoneyFlowPageReqVO;
import com.xiaoshuidi.cloud.module.finance.controller.admin.finance.vo.flow.FinanceMoneyFlowRespVO;
import com.xiaoshuidi.cloud.module.finance.controller.admin.finance.vo.flow.FinanceOrderFeeVO;
import com.xiaoshuidi.cloud.module.finance.controller.admin.finance.vo.reconciliation.*;
import com.xiaoshuidi.cloud.module.finance.convert.finance.FinanceConvert;
import com.xiaoshuidi.cloud.module.finance.dal.dataobject.finance.FinanceMoneyFlowDO;
import com.xiaoshuidi.cloud.module.finance.dal.dataobject.finance.FinanceReconciliationLogDO;
import com.xiaoshuidi.cloud.module.finance.dal.dataobject.finance.FinanceYinlianReconciliationPair;
import com.xiaoshuidi.cloud.module.finance.enums.finance.PaymentSourceEnum;
import com.xiaoshuidi.cloud.module.finance.service.finance.FinanceMoneyFlowService;
import com.xiaoshuidi.cloud.module.finance.service.finance.FinanceReconciliationService;
import com.xiaoshuidi.cloud.module.finance.service.finance.YinlianReconciliationService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.time.LocalDate;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.xiaoshuidi.cloud.framework.common.pojo.CommonResult.success;

@Slf4j
@Tag(name = "管理后台 - 对账")
@RestController
@RequestMapping("/finance/reconciliation")
public class FinanceReconciliationController {

    @Resource
    private FinanceReconciliationService financeReconciliationService;
    @Resource
    private YinlianReconciliationService yinlianReconciliationService;
    @Resource
    private FinanceMoneyFlowService financeMoneyFlowService;

    @Resource
    private ContractRentOrderApi contractRentOrderApi;

    @Value("classpath:reconciliation_tpl.xlsx")
    private org.springframework.core.io.Resource reconciliationTpl;

    @GetMapping("/daily/summary")
    @Operation(summary = "日汇总对账")
    @PreAuthorize("@ss.hasPermission('finance:accounting:reconciliation')")
    public CommonResult<FinanceReconciliationSummaryVO> dailyReconciliation(FinanceReconciliationPageReqVO reqVO) {
        List<FinanceReconciliationLogDO> channels = financeReconciliationService.date(reqVO.getDate());
        if (CollectionUtils.isEmpty(channels)) {
            return CommonResult.success(FinanceReconciliationSummaryVO.defaultValue(reqVO.getDate().toString()));
        }
        FinanceReconciliationSummaryVO result = channels.stream()
                .map(FinanceConvert.INSTANCE::convert)
                .reduce(FinanceReconciliationSummaryVO.defaultValue(reqVO.getDate().toString()), FinanceReconciliationSummaryVO::add);
        return CommonResult.success(result);
    }

    @GetMapping("/calendar")
    @Operation(summary = "对账日历")
    @PreAuthorize("@ss.hasPermission('finance:accounting:reconciliation')")
    public CommonResult<List<String>> calendar(@RequestParam("year") int year, @RequestParam("month") int month) {
        LocalDate date = LocalDate.now().withYear(year).withMonth(month).withDayOfMonth(1);
        List<FinanceReconciliationLogDO> l = financeReconciliationService.calendar(date);
        return CommonResult.success(l.stream().map(f -> f.getSettlementDate().toString()).collect(Collectors.toList()));
    }

    @GetMapping("/daily/page")
    @Operation(summary = "日汇总对账明细")
    @PreAuthorize("@ss.hasPermission('finance:accounting:reconciliation')")
    public CommonResult<PageResult<FinanceReconciliationRespVO>> list(FinanceReconciliationPageReqVO reqVO) {
        PageResult<FinanceYinlianReconciliationPair> page = financeReconciliationService.getPage(reqVO);
        return CommonResult.success(FinanceConvert.INSTANCE.convertToFinanceReconciliationPage(page));
    }

    @GetMapping("/daily/export")
    @Operation(summary = "日汇总报表导出")
    @PreAuthorize("@ss.hasPermission('finance:accounting:reconciliation:export')")
    public void dailyExport(FinanceReconciliationPageReqVO reqVO, HttpServletResponse response) throws IOException {
        List<FinanceReconciliationLogDO> channels = financeReconciliationService.date(reqVO.getDate());
        FinanceReconciliationSummaryVO result;
        if (CollectionUtils.isEmpty(channels)) {
            result = FinanceReconciliationSummaryVO.defaultValue(reqVO.getDate().toString());
        } else {
            result = channels.stream()
                    .map(FinanceConvert.INSTANCE::convert)
                    .reduce(FinanceReconciliationSummaryVO.defaultValue(reqVO.getDate().toString()), FinanceReconciliationSummaryVO::add);
        }
        reqVO.setPageSize(100);
        PageResult<FinanceYinlianReconciliationPair> pageResult = financeReconciliationService.getPage(reqVO);

        ExcelUtils.fill(response, "自动对账明细.xlsx", reconciliationTpl.getInputStream(),
                pageResult, FinanceConvert.INSTANCE::convertToFinanceReconciliationExportVO,
                (pageNo) -> {
                    reqVO.setPageNo(pageNo);
                    return financeReconciliationService.getPage(reqVO);
                }, () -> {
                    Map<String, Object> map = MapUtils.newHashMap();
                    map.put("settlementDate", result.getSettlementDate());
                    map.put("totalAmount", result.getTotalAmount());
                    map.put("totalCount", result.getTotalCount());
                    map.put("channelTotalAmount", result.getChannelTotalAmount());
                    map.put("channelTotalCount", result.getChannelTotalCount());
                    map.put("totalServiceCharge", result.getTotalServiceCharge());
                    map.put("result", result.getResult());
                    return map;
                });
    }

    @GetMapping("/flow")
    @Operation(summary = "交易明细列表")
    @PreAuthorize("@ss.hasPermission('finance:accounting:reconciliation:query')")
    public CommonResult<FinanceReconciliationPageResult> detail(FinanceMoneyFlowPageReqVO reqVO) {
        reqVO.setSourceFrom(PaymentSourceEnum.ONLINE.name());
        PageResult<FinanceMoneyFlowDO> pageResult = financeMoneyFlowService.pageFinanceMoneyFlow(reqVO);
        if (pageResult.getTotal() == 0) {
            return success(new FinanceReconciliationPageResult());
        } else {
            FinanceReconciliationPageResult result = financeMoneyFlowService.getReconciliationFlowSummary(reqVO);
            PageResult<FinanceMoneyFlowRespVO> p = FinanceConvert.INSTANCE.convertToFinanceMoneyFlowPage(pageResult);
            List<FinanceMoneyFlowDO> list = pageResult.getList();
            if (CollectionUtils.isNotEmpty(list)){
                List<Long> orderIds = list.stream().map(FinanceMoneyFlowDO::getSourceBizId).map(Long::parseLong).collect(Collectors.toList());
                CommonResult<List<OrderFeeDetailVO>> feeListByOrderIds = contractRentOrderApi.getFeeListByOrderIds(orderIds);
                if (feeListByOrderIds.isSuccess()){
                    List<OrderFeeDetailVO> data = feeListByOrderIds.getData();
                    Map<Long, List<OrderFeeDetailVO>> orderFeeMap = data.stream().collect(Collectors.groupingBy(OrderFeeDetailVO::getOrderId));
                    List<FinanceMoneyFlowRespVO> moneyFlowRespVOS = p.getList();
                    for (FinanceMoneyFlowRespVO flowResp : moneyFlowRespVOS) {
                        Long sourceBizId = Long.parseLong(flowResp.getSourceBizId());
                        List<OrderFeeDetailVO> orderFeeDetailVOS = orderFeeMap.get(sourceBizId);
                        List<FinanceOrderFeeVO> orderFeeList = FinanceConvert.INSTANCE.convertFeeList(orderFeeDetailVOS);
                        flowResp.setFeeList(orderFeeList);
                    }
                    p.setList(moneyFlowRespVOS);
                }
            }
            result.setTotal(p.getTotal());
            result.setList(p.getList());
            return success(result);
        }
    }

    @GetMapping("/flow/export")
    @Operation(summary = "交易明细导出")
    @PreAuthorize("@ss.hasPermission('finance:accounting:reconciliation:query:export')")
    public void export(FinanceMoneyFlowPageReqVO reqVO, HttpServletResponse response) throws IOException {
        reqVO.setSourceFrom(PaymentSourceEnum.ONLINE.name());
//        reqVO.setPageSize(100);
//        PageResult<FinanceMoneyFlowDO> pageResult = financeMoneyFlowService.getFinanceMoneyFlowPage(reqVO);
//        ExcelUtils.write(response, "交易记录明细.xlsx", "交易记录明细",
//                FinanceReconciliationFlowExcelVO.class,
//                pageResult,
//                FinanceConvert.INSTANCE::convertToFinanceMoneyFlowExcelList2,
//                (pageNo) -> {
//                    reqVO.setPageNo(pageNo);
//                    return financeMoneyFlowService.getFinanceMoneyFlowPage(reqVO);
//                });
        List<FinanceReconciliationFlowExcelVO> list = financeMoneyFlowService.getFinanceMoneyFlowExport(reqVO);
        ExcelUtils.write(response,"交易记录明细.xlsx", "交易记录明细",FinanceReconciliationFlowExcelVO.class,list);
    }

    @PostMapping
    @Operation(summary = "异常备注")
    @PreAuthorize("@ss.hasPermission('finance:accounting:reconciliation')")
    public CommonResult<Void> manualCheck(@Valid @RequestBody FinanceReconciliationManualCheckReqVO reqVO) {
        financeReconciliationService.manualCheck(reqVO);
        return CommonResult.success();
    }
}
